package demo.isolib {
	import as3isolib.core.ClassFactory;
	import as3isolib.display.IIsoView;
	import as3isolib.display.IsoView;
	import as3isolib.display.renderers.DefaultShadowRenderer;
	import as3isolib.display.scene.IsoGrid;
	import as3isolib.display.scene.IsoScene;
	import as3isolib.geom.IsoMath;
	import as3isolib.geom.Pt;
	
	import com.sshc625.log.Logger;
	
	import de.polygonal.ds.NullIterator;
	
	import demo.gemo.GridPoint;
	import demo.isolib.Tile;
	import demo.isolib.movement.BasicMove;
	import demo.isolib.movement.MoveMode;
	import demo.isometric.utils.DragManager;
	
	import eDpLib.events.ProxyEvent;
	
	import flash.display.DisplayObject;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	
	import masputih.pathfinding.astar.AStarGrid;

	/**
	 * World is container which host all nodes.
	 */
	public class World extends IsoView {
		
		private var _groundScene:IsoScene;
		private var _gridScene:IsoScene;
		private var _grid:IsoGrid;
		private var _selectedTile:Tile;
		private var _avatar:Tile;

		public var astarGrid:AStarGrid;
		private var _debugMode:Boolean;

		//  the state of world.
		private var _worldSate:WorldState = WorldState.IDLE;
		private var _prevPt:Pt = new Pt(x, y, 0);
		private var _currPt:Pt = new Pt(x, y, 0);
		private var _isMouseDown:Boolean = false;
		
		public function World(w:Number = 800, h:Number = 600, gridCols:Number = 20, gridRows:Number = 20, gridCellsize:Number = 20) {
			Logger.log("new World V");
			
			this.setSize(w, h);
			this.createGrid(gridCols, gridRows, gridCellsize);
			this.createGroundScene();
			super.addEventListener(Event.ENTER_FRAME, handleEnterFrame);
			super.addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
			this.debugMode = false;
		}
		
		private function onAddedToStage(e:Event):void {
			Logger.log("World.onAddedToStage(" + arguments + ")");
			super.removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
			
			// observers for mouse event.
			super.addEventListener(MouseEvent.MOUSE_DOWN, 		this.onMouseDown, 	false);
			super.addEventListener(MouseEvent.MOUSE_UP, 		this.onMouseUp, 	false);
			super.addEventListener(MouseEvent.CLICK, 			this.onMouseClick, 	false);
			super.addEventListener(MouseEvent.MOUSE_MOVE,		this.onMouseMove, 	false);
		}
		private function onMouseDown(evt:MouseEvent):void {
			Logger.log("World.onMouseDown("+evt+")");
			this._isMouseDown = true;
			
			if( !TileManager.getInstance().hasSelectedTiles()) {
				this.startDrag();
			}
		}
		private function onMouseUp(evt:MouseEvent):void {
			Logger.log("World.onMouseUp("+evt+")");
			this._isMouseDown = false;
			if( !TileManager.getInstance().hasSelectedTiles()) {
				this.stopDrag();
				Logger.log("World.onMouseUp "+this.x+", "+this.y);
			}
		}
		private function onMouseClick(evt:MouseEvent):void {	
			Logger.log("World.onMouseClick("+evt+")");
			// skip avatar moving if the world is in the stage of moving.
			if(this._worldSate == WorldState.MOVING) {
				this._worldSate = WorldState.IDLE;
				return;
			}
			// move avatar if there is no selected tile.
			if ( !TileManager.getInstance().hasSelectedTiles() ) {
				var gridPt:GridPoint = this.screenToGridAndRound(evt.localX, evt.localY);
				this._avatar.moveTo(gridPt.gridX, gridPt.gridY, 0);
			}
		}
		private function onMouseMove(evt:MouseEvent):void {
			if( this._isMouseDown ) {
				this._worldSate = WorldState.MOVING;
			}
			//var gridPt:GridPoint = this.screenToGridAndRound(evt.localX, evt.localY);
			
		}

		//		
		private function handleEnterFrame(e:Event):void {
			super.render(true);
		}
		/**
		 * add avatar to world
		 * 
		 * @parameter col	gridX
		 * @parameter row	gridY
		 */
		public function addAvatar(tile:Tile, col:Number, row:Number):void {
			this._avatar = tile;
			this.pushTile(tile, col, row);
		}

		public function addTile(tile:Tile, col:Number, row:Number):void {
			Logger.log("World.addTile(" + arguments + ")");
			TileManager.getInstance().addTile(tile);
			this.pushTile(tile, col, row);
		}

		private function pushTile(tile:Tile, col:Number, row:Number):void {
			if (col >= this.grid.gridSize[0] || row >= this.grid.gridSize[1]) {
				throw new Error("IllegalArgumentsException, Out fo range");
			}
			tile.world = this;
			tile.spawnAt(col, row, 0);

			//tile.addEventListener(TileEvent.SELECTED, onTileSelected);
			tile.addEventListener(TileEvent.DOWN, this.onTileDown);
			tile.addEventListener(TileEvent.MOVE, this.onTileMove);			
			tile.addEventListener(TileEvent.UP, this.onTileUp);
			//tile.addEventListener(TileEvent.CLICK, this.onTileClick);
				
			this._groundScene.addChild(tile);
		}

		private function onTileClick(e:TileEvent):void {
			//var evt:MouseEvent = e.targetEvent as MouseEvent;
			var tile:Tile = e.currentTarget as Tile;
			tile.selected = !tile.selected;
			Logger.log("World.onTileClick="+tile.name);
			
		}
		
		public function removeTile(tile:Tile):void {
			if (this._groundScene.contains(tile)) {
				this._groundScene.removeChild(tile);
			}
		}

		public function setBackground(bg:DisplayObject, x:Number = 0, y:Number = 0):void {
			bg.x = x;
			bg.y = y;
			backgroundContainer.addChild(bg);
		}

		
		private function onTileMove(e:TileEvent):void {
		//	Logger.log("World.onTileMove(" + e + ")");
			//var tile:Tile = e.currentTarget as Tile;
			//tile.moveBehavior = new BasicMove();
		}
		private function onTileUp(e:TileEvent):void {
			Logger.log("World.onTileUp(" + e + ")");
			var tile:Tile = e.currentTarget as Tile;
			//tile.selected = false;
		}
		private function onTileDown(e:TileEvent):void {
			Logger.log("World.onTileDown(" + e + ")");
			var tile:Tile = e.currentTarget as Tile;
			if( tile != this._avatar ) {
				tile.selected = true;
				if(tile.dragable)
					DragManager.getInstance().drag(tile, this);
			}
		}		
		private function onTileSelected(e:TileEvent):void {
			Logger.log("World.onTileSelected(" + e + ")");
			// 
			if (TileManager.getInstance().hasTile(Tile(e.target))) {
				this._selectedTile = Tile(e.target);
				//DragManager.getInstance().drag(this._selectedTile, this);
			} else {
				this._avatar = Tile(e.target);
			}
		}
		private function isAvatar(t:Tile):Boolean {
			return ( !TileManager.getInstance().hasTile(t) );	
		}
		
		private function createGroundScene():void {
			Logger.log("World.createGroundScene(" + arguments + ")");
			this._groundScene = new IsoScene();
			var factory:as3isolib.core.ClassFactory = new as3isolib.core.ClassFactory(DefaultShadowRenderer);
			factory.properties = {shadowColor:0x000000, shadowAlpha:0.15, drawAll:false};
			this._groundScene.styleRenderers = [factory];
			super.addScene(this._groundScene);
		}

		private function createGrid(cols:Number, rows:Number, cellsize:Number = 20):void {
			Logger.log("World.createGrid(" + arguments + ")");
			_gridScene = new IsoScene();
			var factory:as3isolib.core.ClassFactory = new as3isolib.core.ClassFactory(DefaultShadowRenderer);
			factory.properties = {shadowColor:0x000000, shadowAlpha:0.15, drawAll:false};
			this._gridScene.styleRenderers = [factory];
			super.addScene(_gridScene);
			
			_grid = new IsoGrid();
			_grid.cellSize = cellsize;
			_grid.setGridSize(cols, rows);
			_gridScene.addChild(_grid);

			astarGrid = new AStarGrid(cols, rows);
			astarGrid.setAllWalkable();
		}
		
		public function get grid():IsoGrid {
			return _grid;
		}

		public function get debugMode():Boolean {
			return _debugMode;
		}

		public function set debugMode(value:Boolean):void {
			_debugMode = value;
			if (_debugMode) {
				if (!containsScene(_gridScene)) {
					addSceneAt(_gridScene, 0);
				}
			} else {
				if (containsScene(_gridScene)) {
					removeScene(_gridScene);
				}
			}
		}
		
		
		public function get visibleIsoGrid():IsoGrid {
			return null;
		}

		/**
		 * (isoX, isoY) to (girdX, gridY)
		 */
		public function isoToGrid(pt:Pt):GridPoint {
			var col:Number = Math.floor(pt.x / this.grid.cellSize);
			var row:Number = Math.floor(pt.y / this.grid.cellSize);
			return new GridPoint(col, row);
		}
		public function isoToGridAndRound(pt:Pt):GridPoint {
			var gridPt:GridPoint = this.isoToGrid(pt);
			return this.roundGrid(gridPt);
		}		
		/**
		 * (girdX, gridY) to (isoX, isoY)
		 */		
		public function gridToIso(gridX:int, gridY:int):Pt {
			return new Pt(gridX * this.grid.cellSize, gridY * this.grid.cellSize, 0);
		}
		/**
		 * (girdX, gridY) to (screenX, screenY)
		 */		
		public function gridToScreen(p:GridPoint):Pt {
			var isoPt:Pt = this.gridToIso(p.gridX, p.gridY);
			return IsoMath.isoToScreen(isoPt);
		}		
		/**
		 * (screenX, screenY) to (girdX, gridY)
		 * isometric coordinates 
		 */		
		public function screenToGrid(screenX:Number, screenY:Number):GridPoint {
			var screenPt:Pt = new Pt(screenX, screenY);
			var isoPt:Pt = IsoMath.screenToIso(screenPt);
			return this.isoToGrid(isoPt);
		}
		/**
		 * 
		 */
		public function screenToGridAndRound(screenX:Number, screenY:Number):GridPoint {
			var gridPt:GridPoint = this.screenToGrid(screenX, screenY);
			return this.roundGrid(gridPt);
		}		
		/**
		 * round
		 */
		public function roundGrid(gridPt:GridPoint):GridPoint {
			if(gridPt.gridX < 0)						gridPt.gridX = 0;
			if(gridPt.gridY < 0)						gridPt.gridY = 0;
			if(gridPt.gridX > this.grid.gridSize[0])	gridPt.gridX = this.grid.gridSize[0];
			if(gridPt.gridY > this.grid.gridSize[1])	gridPt.gridY = this.grid.gridSize[1];
			return gridPt;
		}	
	}
}